package cl.interpreter;

import java.util.ArrayList;
import java.util.List;

public class RuntimeStack {
    private final static String RETURN_ADDRESS = "Return Address";
    private final static String FRAME_POINTER = "Old FP";
    
    private List<Variable> stack;
    private List<Integer> frameLocs;
    private int framePointer;
    
    public RuntimeStack(List<Variable> args) {
        stack = new ArrayList<Variable>();
        frameLocs = new ArrayList<Integer>();
        
        try {
            callFunction(args, new StackAddress(RETURN_ADDRESS, null), new StackAddress(FRAME_POINTER, null));
        } catch (Exception e) {
            // should never happen
            e.printStackTrace();
        }
    }
    
    public void addVariable(Variable variable) {
        stack.add(variable);
    }
    
    public int leave() {
        int returnAddress = ((Integer)stack.get(framePointer).getValue()).intValue();
        framePointer = ((Integer)stack.get(framePointer + 1).getValue()).intValue();
        
        while(stack.size() > frameLocs.get(frameLocs.size() - 1)) {
            stack.remove(stack.size() - 1);
        }
        
        frameLocs.remove(frameLocs.size() - 1);
        
        return returnAddress;
    }
    
    public void call(List<Variable> args, int returnAddress) {
        try {
            callFunction(args, new StackAddress(RETURN_ADDRESS, returnAddress), new StackAddress(FRAME_POINTER, Integer.MAX_VALUE * 2 + 1 - framePointer));
        } catch (Exception e) {
            // shouldnt ever happen
            e.printStackTrace();
        }
    }
    
    private void callFunction(List<Variable> args, Variable returnAddress, Variable framePointer) {
        frameLocs.add(stack.size());
        stack.addAll(args);
        
        stack.add(returnAddress);
        stack.add(framePointer);
        
        this.framePointer = stack.size() - 2;
    }
    
    public String toString() {
        String res = "-----------------------\n";
        for(int i = 0; i < stack.size(); i++) {
            res += String.format("<%1$s> %2$s\n", Util.toAddress(Integer.MAX_VALUE * 2 + 1 - i), stack.get(i).toString());
            //res += String.format("<%1$d> %2$s\n", i, stack.get(i).toString());
        }
        
        return res + "-----------------------";
    }
    
    public static void main(String[] args) {
        RuntimeStack stack = new RuntimeStack(new ArrayList<Variable>());
        List<Variable> stackArgs = new ArrayList<Variable>();
        
        try {
            stackArgs.add(new Variable("arg1", 12345, "int"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        //System.out.println(stack);
        stack.call(stackArgs, 15);
        try {
            stack.addVariable(new Variable("a", 'a', "char"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        
        System.out.println(stack);
        stack.leave();
    }
}
